package me.leon.lib4x.rxbus


import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import io.reactivex.subjects.Subject
import java.util.*

/**
 * RxBus
 * Created by gorden on 2016/5/12.
 * update 2017/3/1
 */
class RxBus private constructor() {

    private val subscriptionsByEventType = HashMap<Class<*>, MutableList<Disposable>>()

    private val eventTypesBySubscriber = HashMap<Any, MutableList<Class<*>>>()
    private val stickyData = HashMap<Class<*>, MutableList<Any>>()

    private val subscriberMethodByEventType = HashMap<Class<*>, MutableList<SubscriberMethod>>()

    private val bus: Subject<Any>

    init {
        this.bus = PublishSubject.create<Any>().toSerialized()
    }

    /**
     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
     *
     * @param eventType 事件类型
     * @return return
     */
    fun <T> toObservable(eventType: Class<T>): Flowable<T> {
        return bus.toFlowable(BackpressureStrategy.BUFFER).ofType(eventType)
    }

    /**
     * 根据传递的code和 eventType 类型返回特定类型(eventType)的 被观察者
     *
     * @param code      事件code
     * @param eventType 事件类型
     */
    private fun <T> toObservable(code: Int, eventType: Class<T>): Flowable<T> {
        return bus.toFlowable(BackpressureStrategy.BUFFER).ofType(Message::class.java)
                .filter { o -> o.code == code && eventType.isInstance(o.ob) }.map<Any> { o -> o.ob }.cast(eventType)
    }

    /**
     * 注册
     *
     * @param subscriber 订阅者
     */
    fun register(subscriber: Any) {
        val subClass = subscriber.javaClass
        val methods = subClass.declaredMethods
        for (method in methods) {
            if (method.isAnnotationPresent(Subscribe::class.java)) {
                //获得参数类型
                val parameterType = method.parameterTypes
                //参数不为空 且参数个数为1
                if (parameterType != null && parameterType.size == 1) {

                    var eventType = parameterType[0]

                    when (eventType.toString()) {
                        "int" -> {
                            eventType= Class.forName("java.lang.Integer")
                        }
                        "boolean" -> {
                            eventType= Class.forName("java.lang.Boolean")
                        }
                        else -> {
                        }
                    }

                    addEventTypeToMap(subscriber, eventType)
                    val sub = method.getAnnotation(Subscribe::class.java)
                    val code = sub.code
                    val threadMode = sub.threadMode
                    val stciky = sub.isSticky

                    val subscriberMethod = SubscriberMethod(subscriber, method, eventType, code, threadMode,stciky)
                    addSubscriberToMap(eventType, subscriberMethod)

                    addSubscriber(subscriberMethod)
                } else if (parameterType == null || parameterType.size == 0) {

                    val eventType = BusData::class.java

                    addEventTypeToMap(subscriber, eventType)
                    val sub = method.getAnnotation(Subscribe::class.java)
                    val code = sub.code
                    val threadMode = sub.threadMode
                    val stciky = sub.isSticky
                    val subscriberMethod = SubscriberMethod(subscriber, method, eventType, code, threadMode,stciky)
                    addSubscriberToMap(eventType, subscriberMethod)

                    addSubscriber(subscriberMethod)

                }
            }
        }
    }


    /**
     * 将event的类型以订阅中subscriber为key保存到map里
     *
     * @param subscriber 订阅者
     * @param eventType  event类型
     */
    private fun addEventTypeToMap(subscriber: Any, eventType: Class<*>) {
        var eventTypes: MutableList<Class<*>>? = eventTypesBySubscriber[subscriber]
        if (eventTypes == null) {
            eventTypes = mutableListOf()
            eventTypesBySubscriber[subscriber] = eventTypes
        }

        if (!eventTypes.contains(eventType)) {
            eventTypes.add(eventType)
        }
    }

    /**
     * 将注解方法信息以event类型为key保存到map中
     *
     * @param eventType        event类型
     * @param subscriberMethod 注解方法信息
     */
    private fun addSubscriberToMap(eventType: Class<*>, subscriberMethod: SubscriberMethod) {
        var subscriberMethods: MutableList<SubscriberMethod>? = subscriberMethodByEventType[eventType]
        if (subscriberMethods == null) {
            subscriberMethods = ArrayList()
            subscriberMethodByEventType[eventType] = subscriberMethods
        }

        if (!subscriberMethods.contains(subscriberMethod)) {
            subscriberMethods.add(subscriberMethod)
        }
    }

    /**
     * 将订阅事件以event类型为key保存到map,用于取消订阅时用
     *
     * @param eventType  event类型
     * @param disposable 订阅事件
     */
    private fun addSubscriptionToMap(eventType: Class<*>, disposable: Disposable) {
        var disposables: MutableList<Disposable>? = subscriptionsByEventType[eventType]
        if (disposables == null) {
            disposables = ArrayList()
            subscriptionsByEventType[eventType] = disposables
        }

        if (!disposables.contains(disposable)) {
            disposables.add(disposable)
        }
    }

    /**
     * 用RxJava添加订阅者
     *
     * @param subscriberMethod d
     */
    private fun addSubscriber(subscriberMethod: SubscriberMethod) {
        val flowable: Flowable<*>
        if (subscriberMethod.code == -1) {
            flowable = toObservable(subscriberMethod.eventType)
        } else {
            flowable = toObservable(subscriberMethod.code, subscriberMethod.eventType)
        }
        val subscription = postToObservable(flowable, subscriberMethod)
                .subscribe { o: Any -> callEvent(subscriberMethod, o) }

        if (subscriberMethod.isSticky) {
            stickyData[subscriberMethod.eventType]?.forEach {
                bus.onNext(it)
            }
        }

        addSubscriptionToMap(subscriberMethod.subscriber.javaClass, subscription)
    }

    /**
     * 用于处理订阅事件在那个线程中执行
     *
     * @param observable       d
     * @param subscriberMethod d
     * @return Observable
     */
    private fun postToObservable(observable: Flowable<*>, subscriberMethod: SubscriberMethod): Flowable<*> {
        val scheduler: Scheduler
        when (subscriberMethod.threadMode) {
            ThreadMode.MAIN -> scheduler = AndroidSchedulers.mainThread()

            ThreadMode.NEW_THREAD -> scheduler = Schedulers.newThread()

            ThreadMode.CURRENT_THREAD -> scheduler = Schedulers.trampoline()
        }
        return observable.observeOn(scheduler)
    }

    /**
     * 回调到订阅者的方法中
     *
     * @param method code
     * @param object obj
     */
    private fun callEvent(method: SubscriberMethod, ob: Any) {
        val eventClass = ob.javaClass
        val methods = subscriberMethodByEventType[eventClass]
        if (methods != null && methods.size > 0) {
            for (subscriberMethod in methods) {
                val sub = subscriberMethod.method.getAnnotation(Subscribe::class.java)
                val c = sub.code
                if (c == method.code && method.subscriber == subscriberMethod.subscriber && method.method == subscriberMethod.method) {
                    subscriberMethod.invokes(ob)
                }
            }
        }
    }


    /**
     * 是否注册
     *
     * @param subscriber
     * @return
     */
    @Synchronized
    fun isRegistered(subscriber: Any): Boolean {
        return eventTypesBySubscriber.containsKey(subscriber)
    }

    /**
     * 取消注册
     *
     * @param subscriber object
     */
    fun unregister(subscriber: Any) {
        val subscribedTypes = eventTypesBySubscriber[subscriber]
        if (subscribedTypes != null) {
            for (eventType in subscribedTypes) {
                unSubscribeByEventType(subscriber.javaClass)
                unSubscribeMethodByEventType(subscriber, eventType)
            }
            eventTypesBySubscriber.remove(subscriber)
        }
    }

    /**
     * subscriptions unsubscribe
     *
     * @param eventType eventType
     */
    private fun unSubscribeByEventType(eventType: Class<*>) {
        val disposables = subscriptionsByEventType[eventType]
        disposables?.forEach {
            if (!it.isDisposed) it.dispose()
        }
    }

    /**
     * 移除subscriber对应的subscriberMethods
     *
     * @param subscriber subscriber
     * @param eventType  eventType
     */
    private fun unSubscribeMethodByEventType(subscriber: Any, eventType: Class<*>) {
        val subscriberMethods = subscriberMethodByEventType[eventType]
        subscriberMethods?.forEach {
            if (it.subscriber == subscriber) {
                subscriberMethods.remove(it)
            }
        }
    }

    fun send(code: Int, o: Any) {
        bus.onNext(Message(code, o))
    }

    fun post(o: Any) {
        bus.onNext(o)
    }

    fun postSticky(o: Any) {
        if (stickyData[o.javaClass] != null)
            stickyData[o.javaClass]?.add(o)
        else
            stickyData[o.javaClass] = mutableListOf(o)
        println("postStick ${o.javaClass}")
        bus.onNext(o)
    }

    fun unRegisterSticky(clazz:Class<*>){

        stickyData.remove(clazz)

    }

    fun send(code: Int) {
        bus.onNext(Message(code, BusData()))
    }

    inner class Message @JvmOverloads constructor(var code: Int, var ob: Any?)


    companion object {
        val LOG_BUS = "RXBUS_LOG"
        @Volatile
        private var defaultInstance: RxBus? = null

        val default: RxBus
            get() {
                var rxBus = defaultInstance
                if (defaultInstance == null) {
                    synchronized(RxBus::class.java) {
                        rxBus = defaultInstance
                        if (defaultInstance == null) {
                            rxBus = RxBus()
                            defaultInstance = rxBus
                        }
                    }
                }
                return rxBus!!
            }
    }
}
